/**
 * Project: D20 Campaigner
 * Created: Jul 4, 2005 by bebopJMM
 *------------------------------------------------------------------------------
 */
package org.rollinitiative.d20.spell;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

import javax.xml.transform.OutputKeys;

import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlException;
import org.exist.xmldb.XQueryService;
import org.rollinitiative.d20.persist.PersistenceBridge;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XMLResource;

import org.rollinitiative.d20.schema.resource.ResourceCollectionDocument;
import org.rollinitiative.d20.schema.resource.ResourceCollectionType;
import org.rollinitiative.d20.schema.spells.SpellDescriptType;
import org.rollinitiative.d20.schema.spells.SpellIndexType;

/**
 * This class bridges the domains of data content and data persistence for Spell
 * related content.
 * 
 * @author bebopJMM
 * @since rel-0.1
 */
public class SpellContentBridge extends PersistenceBridge {

	final static Logger LOGGER_ = Logger.getLogger(SpellContentBridge.class);

	XQueryService xqueryService_;

	Collection spellsCollection_;

	public SpellContentBridge(XMLConfiguration config) {
		super(config);
	}

	/**
	 * This method unmarshall an xml SpellLibrary resource into it's xmlBean.
	 * 
	 * @param sourceFile
	 * @return
	 */
	public static ResourceCollectionType unmarshallSpellLibrary(File sourceFile) {
		// Attempt to construct xmlBean from document.
		LOGGER_.info("Attempting to unmarshall: " + sourceFile.getName());
		FileInputStream fileStream = null;
		try {
			// Unmarshall the xml.
			fileStream = new FileInputStream(sourceFile);
			ResourceCollectionDocument libraryDoc = ResourceCollectionDocument.Factory
					.parse(fileStream);
			fileStream.close();
			return libraryDoc.getResourceCollection();
		} catch (Throwable ex) {
			LOGGER_
					.error("Failed to load SpellLibrary from: " + sourceFile,
							ex);
			return null;
		}
	}

	public void loadSpellsCollection(String campaign) {
		try {
			connect(campaign);
			spellsCollection_ = retrieveCollection("spells");
			xqueryService_ = (XQueryService) spellsCollection_.getService(
					"XQueryService", "1.0");
		} catch (XMLDBException ex) {
			LOGGER_.error("Failed to load the spells collection");
		}
	}

	public String[] getSpellElements() {
		String[] elements = { "Name", "Caster Type", "Level", "School" };
		return elements;
	}

	public SpellIndexType[] getIndex() {
		String indexQuery = config.getString("spellBridge.queries.index");
		ResourceSet results = querySpells(indexQuery);
		SpellIndexType[] index = null;
		try {
			int nSpells = (int) results.getSize();
			index = new SpellIndexType[nSpells];
			for (int i = 0; i < nSpells; i++) {
				// LOGGER_.debug("result resource: " +
				// results.getResource(i).getContent());
				index[i] = SpellIndexType.Factory.parse(results.getResource(i)
						.getContent().toString(), xmlOptions);
				// LOGGER_.debug("Parsed spell name = " + index[i].getName());
			}
		} catch (XMLDBException xmldbEx) {
			LOGGER_.error("Failure processing ResourceSet", xmldbEx);
			return null;
		} catch (XmlException xmlEx) {
			LOGGER_.error("Failure marshalling into xmlbean", xmlEx);
			return null;
		}
		return index;
	}

	public SpellDescriptType getSpell(String spellName, String caster, int level) {
		String indexQuery = config
				.getString("spellBridge.queries.spellByName");
		indexQuery = indexQuery.replace("#NAME#", spellName);
		indexQuery = indexQuery.replace("#CASTER#", caster);
		indexQuery = indexQuery.replace("#LEVEL#", Integer.toString(level));

		LOGGER_.debug("Parameterized Query: " + indexQuery);
		ResourceSet results = querySpells(indexQuery);
		try {
			SpellDescriptType spell = SpellDescriptType.Factory.parse(results
					.getResource(0).getContent().toString(), xmlOptions);
			return spell;
		} catch (XmlException xmlEx) {
			LOGGER_.error("Failure marshalling into xmlbean", xmlEx);
			return null;
		} catch (XMLDBException xmldbEx) {
			LOGGER_.error("Failure processing ResourceSet", xmldbEx);
			return null;
		}

	}

	public SpellDescriptType getSpell(String spellID) {
		String indexQuery = config.getString("spellBridge.queries.spellByID");
		indexQuery = indexQuery.replace("#ID#", spellID);

		LOGGER_.debug("Parameterized Query: " + indexQuery);
		ResourceSet results = querySpells(indexQuery);
		try {
			SpellDescriptType spell = SpellDescriptType.Factory.parse(results
					.getResource(0).getContent().toString(), xmlOptions);
			return spell;
		} catch (XmlException xmlEx) {
			LOGGER_.error("Failure marshalling into xmlbean", xmlEx);
			return null;
		} catch (XMLDBException xmldbEx) {
			LOGGER_.error("Failure processing ResourceSet", xmldbEx);
			return null;
		}

	}

	ResourceSet querySpells(String xquery) {
		long start = System.currentTimeMillis();
		ResourceSet results = null;
		try {
			results = xqueryService_.query(xquery);
			long qtime = System.currentTimeMillis() - start;
			start = System.currentTimeMillis();

			Properties outputProperties = new Properties();
			outputProperties.setProperty(OutputKeys.INDENT, "yes");
			for (int i = 0; i < (int) results.getSize(); i++) {
				XMLResource resource = (XMLResource) results
						.getResource((long) i);
			}
			long rtime = System.currentTimeMillis() - start;
			LOGGER_.info("hits: " + results.getSize());
			LOGGER_.info("query time:    " + qtime);
			LOGGER_.info("retrieve time: " + rtime);
		} catch (XMLDBException e) {
			// TODO Auto-generated catch block
			LOGGER_.error("Failure for query: " + xquery, e);
		}
		return results;
	}

}
